package com.mandy.datamerge.aspect;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.WeakCache;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.mandy.datamerge.annotation.DataMergeField;
import com.mandy.datamerge.spring.ApplicationContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @className DataMergeAspect
 * @description 切面处理注解
 * @author jianyongjie@126.com
 * @date 2020/7/3 9:06 上午
 * @version 1.0.0
 **/
@Aspect
@Component
@Slf4j
public class DataMergeAspect {

    /**弱引用，缓存30分钟**/
    final static WeakCache<Class, Map<Field, DataMergeField>> CACHE_FIELDS = CacheUtil.newWeakCache(DateUnit.MINUTE.getMillis() * 30);

    /**线程私有变量，用来存储第一次请求需要解析的对象，解决循环依赖**/
    final static ThreadLocal<Object> CURRENT_THREAD = new ThreadLocal<>();

    @Around("@annotation(com.mandy.datamerge.annotation.DataMergeMark)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Object proceed = point.proceed();
        if (proceed == null || ObjectUtil.isBasicType(point)) {
            return point;
        }

        //首次将对象保存到线程私有变量中，暂时解决循环依赖问题
        if (CURRENT_THREAD.get() == null) {
            CURRENT_THREAD.set(proceed);
        }

        if (CURRENT_THREAD.get() == proceed) {
            if (proceed instanceof Collection) {
                Collection result = (Collection) proceed;
                for (Object item : result) {
                    handler(item);
                }
            } else if (proceed.getClass().isArray()) {
                Object[] result = (Object[]) proceed;
                for (Object item : result) {
                    handler(item);
                }
            } else if (proceed instanceof Map) {

            } else {
                handler(proceed);
            }
        }
        return proceed;
    }

    @After(value = "@annotation(com.mandy.datamerge.annotation.DataMergeMark)")
    public void after(JoinPoint joinPoint) {
        log.info("thread local remove...");
        if (CURRENT_THREAD.get() != null) {
            CURRENT_THREAD.remove();
        }
    }

    private void handler(Object item) {
        try {
            //该条数据中所有field 的 Annotation
            Map<Field, DataMergeField> fieldAnnotation = getFieldAnnotation(item.getClass());
            fieldAnnotation.forEach((field, annotation) -> {
                //业务类
                Object service = ApplicationContextHolder.getBean(annotation.service());
                //业务类方法
                String method = annotation.method();
                //业务方法参数（如果为 null 则是无惨调用）
                String[] paramNames = annotation.paramNames();
                //业务方法返回值属性 (如果为null ,则整个对象 set 进去）
                String extractField = annotation.extractField();

                List<Object> params = Lists.newArrayListWithCapacity(paramNames.length);
                if (ArrayUtil.isNotEmpty(paramNames)) {
                    //参数值
                    for (String paramName : paramNames) {
                        Object paramValue = ReflectUtil.getFieldValue(item, paramName);
                        if (Objects.nonNull(paramValue)) {
                            params.add(ReflectUtil.getFieldValue(item, paramName));
                        }
                    }
                }

                //TODO 判断返回的值和绑定属性类型是否一致
                Object invoke = ReflectUtil.invoke(service, method, params.toArray());

                Object setValue = StrUtil.isEmpty(extractField) ? invoke : ReflectUtil.getFieldValue(invoke, extractField);
                ReflectUtil.setFieldValue(item, field.getName(), setValue);
            });
        } catch (Exception e) {
            log.warn("异常信息:{}", e.getMessage());
        }
    }

    private Map<Field, DataMergeField> getFieldAnnotation(Class clazz) {
        Map<Field, DataMergeField> cacheFieldAnn = CACHE_FIELDS.get(clazz);

        if (CollectionUtil.isEmpty(cacheFieldAnn)) {
            Field[] fields = ReflectUtil.getFields(clazz);

            cacheFieldAnn = Stream.of(fields)
                    .filter(x -> x.isAnnotationPresent(DataMergeField.class))
                    .map(x -> new AbstractMap.SimpleEntry<>(x, x.getAnnotation(DataMergeField.class)))
                    .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, v -> v.getValue(), (v1, v2) -> v2));
            CACHE_FIELDS.put(clazz, cacheFieldAnn);
        }
        return cacheFieldAnn;
    }
}
